#include <algorithm>
#include "BasicAIModule.h"
#include "../Addons/Util.h"
#include "EngageManager.h"
using namespace BWAPI;

/////// DEBUG!



void CreateMiniDump( EXCEPTION_POINTERS* pep ) 
{
  // Open the file 

  HANDLE hFile = CreateFile( _T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE, 
    0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); 

  if( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ) 
  {
    // Create the minidump 

    MINIDUMP_EXCEPTION_INFORMATION mdei; 

    mdei.ThreadId           = GetCurrentThreadId(); 
    mdei.ExceptionPointers  = pep; 
    mdei.ClientPointers     = FALSE; 

    MINIDUMP_TYPE mdt       = (MINIDUMP_TYPE) (
                              MiniDumpWithDataSegs|
                              MiniDumpWithHandleData|
                              MiniDumpWithFullMemoryInfo|
                              MiniDumpWithThreadInfo|
                              MiniDumpWithUnloadedModules); 

    BOOL rv = MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), 
      hFile, mdt, (pep != 0) ? &mdei : 0, 0, 0 ); 

    if( !rv ) 
      _tprintf( _T("MiniDumpWriteDump failed. Error: %u \n"), GetLastError() ); 
    else 
      _tprintf( _T("Minidump created.\n") ); 

    // Close the file 

    CloseHandle( hFile ); 

  }
  else 
  {
    _tprintf( _T("CreateFile failed. Error: %u \n"), GetLastError() ); 
  }

}
LONG WINAPI MyUnhandledExceptionFilter(
 struct _EXCEPTION_POINTERS *ExceptionInfo
)
 {
     CreateMiniDump(ExceptionInfo);
     return EXCEPTION_EXECUTE_HANDLER;
 }

/////


bool BasicAIModule::analyzed = false;
BasicAIModule* theModule = 0; // Allow external access for analyze thread

void BasicAIModule::onStart()
{
  theModule = this;
  this->analyzed = false;
  if (Broodwar->isReplay()) return;

  
//////DEBUG!!!/////////
  SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);

  // My comrades,
  // the line below will generate an access violation exception.
  // Uncomment it, build, and run the DLL, and see if a minidump file gets generated
  // (It should be in the StarCraft folder, MiniDump.dmp)
  // Then, see if you can open this dump file using VC++ 2008
  // and then see if you can run/walk through it,
  // and if it can show you this line, where the error occurs.
  // I got this working on my machine already.
  // *( (int*)0) = 1;


  ///////

  // Enable some cheat flags
  Broodwar->enableFlag(Flag::UserInput);
  //Broodwar->enableFlag(Flag::CompleteMapInformation);

  BWTA::readMap();
  // start analyze thread
  this->analyzeThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AnalyzeThread, (LPVOID)&this->bases, 0, NULL);

  this->buildManager       = new BuildManager(&this->arbitrator);
  this->techManager        = new TechManager(&this->arbitrator);
  this->upgradeManager     = new UpgradeManager(&this->arbitrator);
  this->scoutManager       = new ScoutManager(&this->arbitrator);
  this->workerManager      = new WorkerManager(&this->arbitrator);
  this->supplyManager      = new SupplyManager();
  this->buildOrderManager  = new BuildOrderManager(this->buildManager,this->techManager,this->upgradeManager,this->workerManager,this->supplyManager);
  this->attackManager      = new AttackManager(&this->arbitrator);
  this->defenseManager     = DefenseManager::create(&this->arbitrator);
  this->engageManager      = EngageManager::create();
  this->baseManager        = BaseManager::create();
  this->informationManager = InformationManager::create();
  this->borderManager      = BorderManager::create();
  this->unitGroupManager   = UnitGroupManager::create();
  this->enhancedUI         = new EnhancedUI();

  this->supplyManager->setBuildManager(this->buildManager);
  this->supplyManager->setBuildOrderManager(this->buildOrderManager);
  this->techManager->setBuildingPlacer(this->buildManager->getBuildingPlacer());
  this->upgradeManager->setBuildingPlacer(this->buildManager->getBuildingPlacer());
  this->workerManager->setBaseManager(this->baseManager);
  this->workerManager->setBuildOrderManager(this->buildOrderManager);
  this->baseManager->setBuildOrderManager(this->buildOrderManager);
  this->baseManager->setBorderManager(this->borderManager);
  
  this->defenseManager->setBorderManager(this->borderManager);
  TheDefenseManager->setBuildOrderManager(this->buildOrderManager);

  BWAPI::Race race = Broodwar->self()->getRace();
  BWAPI::Race enemyRace = Broodwar->enemy()->getRace();
  BWAPI::UnitType workerType=race.getWorker();

  this->firstExpand = true; 
  this->lastExpand = 0;
  this->larvaThreshold = 0;
  this->buildOrderManager->enableDependencyResolver();

  // ---------------------------------------------------------------------------
  // Modified build order
  // (count, type, priority)
  // ---------------------------------------------------------------------------
  this->buildOrderManager->build(9,UnitTypes::Zerg_Drone,82);
  this->buildOrderManager->build(2,UnitTypes::Zerg_Overlord,81);
  this->buildOrderManager->build(12,UnitTypes::Zerg_Drone,80);
  this->buildOrderManager->build(1,UnitTypes::Zerg_Spawning_Pool,81);
  this->buildOrderManager->build(12,UnitTypes::Zerg_Zergling,80); // deals with zerg rush
  this->buildOrderManager->build(1,UnitTypes::Zerg_Extractor,78);
  this->buildOrderManager->build(20,UnitTypes::Zerg_Drone,76);
  this->buildOrderManager->build(14,UnitTypes::Zerg_Zergling,75);
  this->buildOrderManager->build(9,UnitTypes::Zerg_Mutalisk, 55);
  this->buildOrderManager->buildAdditional(1,UnitTypes::Zerg_Hatchery,50);

  this->buildOrderManager->build(100,UnitTypes::Zerg_Mutalisk, 25);
  this->buildOrderManager->build(100,UnitTypes::Zerg_Zergling, 25);
  // mobile AA
  this->buildOrderManager->build(16,UnitTypes::Zerg_Hydralisk,23);

  this->workerManager->enableAutoBuild();
  this->workerManager->setAutoBuildPriority(40);
  this->baseManager->setRefineryBuildPriority(30);


  // ---------------------------------------------------------------------------
  // Scout Information 
  // (priority, foodCount, UnitType) - remember food counts are doubled
  // ---------------------------------------------------------------------------
  this->scoutManager->addJob(30, 0, UnitTypes::Zerg_Overlord);
  // this->scoutManager->addJob(30, 18, UnitTypes::Zerg_Drone);
  // this->scoutManager->addJob(30, 40, UnitTypes::Zerg_Zergling);



  // ---------------------------------------------------------------------------
  // Reactionary Unit Building
  // ---------------------------------------------------------------------------
  // TODO: need a lot more option to make this scheme flexible:
  // - Cooldown time before reapplying rule?
  // - Rallying point for built units?
  // (triggeringType, triggeringThreshold, reactionType, reactionRatio)
  // vs Protoss
  this->buildOrderManager->addReactor(UnitTypes::Protoss_Zealot, 1, UnitTypes::Zerg_Sunken_Colony, 0.3f); 
  this->buildOrderManager->addReactor(UnitTypes::Protoss_Zealot, 2, UnitTypes::Zerg_Zergling, 4);
  this->buildOrderManager->addReactor(UnitTypes::Protoss_Archon, 1, UnitTypes::Zerg_Guardian, 0.65f); 
  this->buildOrderManager->addReactor(UnitTypes::Protoss_Carrier, 1, UnitTypes::Zerg_Scourge, 4);
  this->buildOrderManager->addReactor(UnitTypes::Protoss_Scout, 1, UnitTypes::Zerg_Spore_Colony, .7f); 
  // vs Terran
  this->buildOrderManager->addReactor(UnitTypes::Terran_Battlecruiser, 3, UnitTypes::Zerg_Scourge, 7);
  this->buildOrderManager->addReactor(UnitTypes::Terran_Battlecruiser, 1, UnitTypes::Zerg_Devourer, 2);
  this->buildOrderManager->addReactor(UnitTypes::Terran_Siege_Tank_Tank_Mode, 2, UnitTypes::Zerg_Guardian, 0.15f); 
  this->buildOrderManager->addReactor(UnitTypes::Terran_Marine, 3, UnitTypes::Zerg_Sunken_Colony, 0.25f); 
  this->buildOrderManager->addReactor(UnitTypes::Terran_Valkyrie, 2, UnitTypes::Zerg_Scourge, .6f); 
  this->buildOrderManager->addReactor(UnitTypes::Terran_Wraith, 1, UnitTypes::Zerg_Spore_Colony, .5f);
  // vs Zerg
  this->buildOrderManager->addReactor(UnitTypes::Zerg_Mutalisk, 2, UnitTypes::Zerg_Spore_Colony, .5f); 
    
  

  // ---------------------------------------------------------------------------
  // Auto Upgrades
  // add[Enemy]Upgrade(upgradeType, threshold, unitType, priority=100, level=1)
  // ---------------------------------------------------------------------------
  // If you use addEnemyUpgrade, it will check the unitType count 
  // against the enemy's units instead of yours
  // see here for list of upgrades: http://code.google.com/p/bwapi/wiki/UpgradeTypes
  //this->buildOrderManager->addUpgrade(UpgradeTypes::Grooved_Spines, 10, UnitTypes::Zerg_Hydralisk, 40);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Pneumatized_Carapace, 10, UnitTypes::Zerg_Overlord, 94); // very important
   this->buildOrderManager->addUpgrade(UpgradeTypes::Antennae, 10, UnitTypes::Zerg_Overlord, 40);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Metabolic_Boost, 2, UnitTypes::Zerg_Zergling, 30);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Adrenal_Glands, 40, UnitTypes::Zerg_Zergling, 10, 1);
  
 
  // Warning: These are afflicted by a bug in BWAPI that forces us to build a
  // Lair or Spire, as the upgraded versions will not count
  this->buildOrderManager->addUpgrade(UpgradeTypes::Pneumatized_Carapace, 10, UnitTypes::Zerg_Overlord, 94); // very important
  this->buildOrderManager->addUpgrade(UpgradeTypes::Antennae, 10, UnitTypes::Zerg_Overlord, 40);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Zerg_Melee_Attacks, 20, UnitTypes::Zerg_Zergling, 20, 3);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Zerg_Carapace, 10, UnitTypes::Zerg_Zergling, 20, 3);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Zerg_Flyer_Attacks, 12, UnitTypes::Zerg_Mutalisk, 60, 1);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Zerg_Flyer_Attacks, 20, UnitTypes::Zerg_Mutalisk, 40, 3);
  this->buildOrderManager->addUpgrade(UpgradeTypes::Zerg_Flyer_Carapace, 25, UnitTypes::Zerg_Mutalisk, 10, 3); // Low priority



  // ---------------------------------------------------------------------------
  // Debug
  // ---------------------------------------------------------------------------
  this->scoutManager->setDebugMode(true);
  this->buildManager->setDebugMode(true);
  // this->buildOrderManager->setDebugMode(true); // too much text
  this->showManagerAssignments = true;


  // // temp debug - getting all dependencies
  // FILE *fp = fopen("debug.txt", "w");
  // fprintf(fp, "Units----------------------------\n");
  // for each (UnitType type in BWAPI::UnitTypes::allUnitTypes()) {
  //   fprintf(fp, "%s:\n", type.getName().c_str());
  //   for each (std::pair<UnitType, int> unit in type.requiredUnits()){
  //     fprintf(fp, "   Unit: %s\n", unit.first.getName().c_str());
  //   }
  //   if (type.requiredTech() != TechTypes::None) {
  //     fprintf(fp, "   Tech: %s\n", type.requiredTech().getName().c_str());
  //   }
  // }
  // fprintf(fp, "Upgrades-------------------------\n");
  // for each (UpgradeType type in BWAPI::UpgradeTypes::allUpgradeTypes()) {
  //   fprintf(fp, "%s:\n", type.getName().c_str());
  //   for (int i=1;i<type.maxRepeats()+1;i++) {
  //     if (type.whatsRequired(i) != UnitTypes::None) {
  //       fprintf(fp, "   (%d): %s\n", i, type.whatsRequired(i).getName().c_str());
  //     }
  //   }
  // }
  // fclose(fp);
}

BasicAIModule::~BasicAIModule()
{
  // stop the thread
  SuspendThread(this->analyzeThreadHandle);

  delete this->buildManager;
  delete this->techManager;
  delete this->upgradeManager;
  delete this->scoutManager;
  delete this->workerManager;
  delete this->supplyManager;
  delete this->buildOrderManager;
  delete this->attackManager;
  DefenseManager::destroy();
  EngageManager::destroy();
  InformationManager::destroy();
  BorderManager::destroy();
  UnitGroupManager::destroy();
  delete this->enhancedUI;
}
void BasicAIModule::onEnd(bool isWinner)
{
  log("onEnd(%d)\n",isWinner);
}
void BasicAIModule::onFrame()
{
  int timeStart = GetTickCount();
  int moduleTimeStart;
  if (Broodwar->isReplay()) return;

  // Update WorkerManager's auto build based on Hatch count, but don't go over 80
  int numHatcheries = SelectAll()(isResourceDepot).size();
  int numRefineries = SelectAll()(isRefinery).size();
  this->workerManager->setAutoBuildMax(min(80, numHatcheries*5 + numRefineries*3 + 10));

  // Update larva threshold for in base expansion limitations - we can do this
  // better with income and current resources, but I'm going to approximate it
  // now with food count. 6 at 50 food, 15 at 200 food (linearly) and 40 at cap
  // Using: (supp - minFood) * (maxLarv - minLarv) / (maxFood - minFood) + minLarv
  this->larvaThreshold = (Broodwar->self()->supplyTotal() - 100) * (15 - 6) / (400 - 100) + 6;
  // Make sure our minimum is 6
  larvaThreshold = max(6, larvaThreshold); 
  // At food cap, build more hatcheries to replenish army faster
  if (Broodwar->self()->supplyUsed() >= 400)
    larvaThreshold = 40;

  // Scout more often - every N seconds right now if we don't have a scout
  int scoutFreq = 120; // in seconds, for readability
  int scoutCount = max(2, Broodwar->getStartLocations().size()); // more bases = more scouts
  if (this->analyzed && Broodwar->getFrameCount() % (scoutFreq*24) == 0 && this->scoutManager->numScouts() < scoutCount) {
    this->scoutManager->addJob(30, 0, UnitTypes::Zerg_Overlord);
  }

  // Modules that do not depend on analysis
  this->buildManager->update();
  moduleTimeStart = GetTickCount();
  this->buildOrderManager->update();
  reportTime(0, moduleTimeStart);
  this->enhancedUI->update();
  this->engageManager->update();

  // Make sure we don't execute these if we do not have BWTA data yet
  if (this->analyzed) {
    this->baseManager->update();
    this->techManager->update();
    this->upgradeManager->update();
    moduleTimeStart = GetTickCount();
    this->scoutManager->update();
    reportTime(1, moduleTimeStart);
    this->borderManager->update();

    moduleTimeStart = GetTickCount();
    this->defenseManager->update();
    reportTime(2, moduleTimeStart);
    this->attackManager->update();
  }

  // Modules that do not depend on analysis but may depend on modules after analysis
  moduleTimeStart = GetTickCount();
  this->workerManager->update();
  reportTime(3, moduleTimeStart);
  this->supplyManager->update();

  this->arbitrator.update();

  // Checking for when to expand
  this->evaluateExpand();

  // draw Arbitrator assignments
  std::set<Unit*> units=Broodwar->self()->getUnits();
  if (this->showManagerAssignments)
  {
    for(std::set<Unit*>::iterator i=units.begin();i!=units.end();i++)
    {
      if (this->arbitrator.hasBid(*i))
      {
        int x=(*i)->getPosition().x();
        int y=(*i)->getPosition().y();
        std::list< std::pair< Arbitrator::Controller<BWAPI::Unit*,double>*, double> > bids=this->arbitrator.getAllBidders(*i);
        
        Broodwar->drawTextMap(x,y, "%d", (*i)->getID());
        int y_off=15;
        bool first = false;
        const char activeColor = '\x07', inactiveColor = '\x16';
        char color = activeColor;
        for(std::list< std::pair< Arbitrator::Controller<BWAPI::Unit*,double>*, double> >::iterator j=bids.begin();j!=bids.end();j++)
        {
          Broodwar->drawTextMap(x,y+y_off,"%c%s: %d",color,j->first->getShortName().c_str(),(int)j->second);
          y_off+=15;
          color = inactiveColor;
        }
      }
    }
  }

  // draw APM text
  int apm = Broodwar->getAPM(true);
  Broodwar->drawTextScreen(5,  0, "Current FPS: %d", Broodwar->getFPS());
  Broodwar->drawTextScreen(5, 16, "Current APM:");
  Broodwar->drawTextScreen(5, 32, "  %d commands", apm);
  Broodwar->drawTextScreen(5, 48, "  %d selections", Broodwar->getAPM(false)-apm);
 
  Broodwar->drawTextScreen(155, 0, "Current Build Counts:");
  Broodwar->drawTextScreen(155, 16, "  %d Larva", SelectAll(UnitTypes::Zerg_Larva).size());
  Broodwar->drawTextScreen(155, 32, "  %d Drones", SelectAll(UnitTypes::Zerg_Drone).size());
  Broodwar->drawTextScreen(155, 48, "  %d Overlords", SelectAll(UnitTypes::Zerg_Overlord).size());
  Broodwar->drawTextScreen(155, 64, "  %d Zerglings", SelectAll(UnitTypes::Zerg_Zergling).size());
  Broodwar->drawTextScreen(155, 80, "  %d Mutalisks", SelectAll(UnitTypes::Zerg_Mutalisk).size());
  Broodwar->drawTextScreen(155, 96, "  %d Hatcheries", this->buildOrderManager->getPlannedCount(UnitTypes::Zerg_Hatchery));
  Broodwar->drawTextScreen(155, 112, "  %d MetaUnit*", this->buildOrderManager->MetaUnitPointers.size());

  Broodwar->drawTextScreen(275, 0, "\x0F%s - %s", Broodwar->mapFileName().c_str(), Broodwar->mapHash().substr(0, 5).c_str());
  Broodwar->drawTextScreen(275, 16, "\x0FZerg v %s", Broodwar->enemy()->getRace().getName().c_str());
  Broodwar->drawTextScreen(275, 32, "\x0F%d:%02d", Broodwar->getFrameCount() / (24*60), (Broodwar->getFrameCount() % (24*60)) / 24);

  // Draw mineral/gas text as the BuildOrderManager sees it - red means limited
  Broodwar->drawTextScreen(452, 20, "%s%d", this->buildOrderManager->getMineralLimited()?"\x08":"", this->buildOrderManager->getVisualizedMinerals());
  Broodwar->drawTextScreen(520, 20, "%s%d", this->buildOrderManager->getGasLimited()?"\x08":"", this->buildOrderManager->getVisualizedGas());
  // Draw order queue
  int line = 0;
  for each (std::pair<std::string, int> item in informationManager->itemOrders) {
    if (item.second > 0) {
      Broodwar->drawTextScreen(500, 36 + (line++)*16, "%d %s", item.second, item.first.c_str());
    }
  }

  // Calculate timer
  // Time thresholds are as follows:
  //    >=   2 frames exceed 10 seconds 
  // or >=  10 frames exceed 1 second 
  // or >= 200 frames exceed 55 milliseconds
  int timeElapse = GetTickCount() - timeStart;
  if (timeElapse > 10000)
      timeMonitor[10000]++;
  else if(timeElapse > 1000)
      timeMonitor[1000]++;
  else if(timeElapse > 55)
      timeMonitor[55]++;

  // Draw results
  // KEY: (buildOrderManager, scoutManager, defenseManager, workerManager)
  Broodwar->drawTextScreen(500, 252, "Long frames counter:");
  Broodwar->drawTextScreen(500, 268, "  %s>10s: %d (%d/%d/%d/%d)", timeMonitor[10000]>=2?"\x08":"", timeMonitor[10000], moduleMonitor[10000][0], moduleMonitor[10000][1], moduleMonitor[10000][2], moduleMonitor[10000][3]);
  Broodwar->drawTextScreen(500, 284, "  %s>1s: %d (%d/%d/%d/%d)", timeMonitor[1000]>=10?"\x08":"", timeMonitor[1000], moduleMonitor[1000][0], moduleMonitor[1000][1], moduleMonitor[1000][2], moduleMonitor[1000][3]);
  Broodwar->drawTextScreen(500, 300, "  %s>55ms: %d (%d/%d/%d/%d)", timeMonitor[55]>=200?"\x08":"", timeMonitor[55], moduleMonitor[55][0], moduleMonitor[55][1], moduleMonitor[55][2], moduleMonitor[55][3]);
}

void BasicAIModule::onExpand(BWTA::BaseLocation* target)
{
  /**if (this->firstExpand) {
    // Place creep colonies at an early first expansion
    this->buildOrderManager->build(2, UnitTypes::Zerg_Creep_Colony, 40, target->getTilePosition());
  }*/
  // we have expanded at least once already, so we're done with this flag
  firstExpand = false;
}

void BasicAIModule::onUnitDestroy(BWAPI::Unit* unit)
{
  if (Broodwar->isReplay()) return;
  this->arbitrator.onRemoveObject(unit);
  this->buildManager->onRemoveUnit(unit);
  this->techManager->onRemoveUnit(unit);
  this->upgradeManager->onRemoveUnit(unit);
  this->workerManager->onRemoveUnit(unit);
  this->scoutManager->onRemoveUnit(unit);
  this->defenseManager->onRemoveUnit(unit);
  this->attackManager->onRemoveUnit(unit);
  this->informationManager->onUnitDestroy(unit);
  this->baseManager->onRemoveUnit(unit);
  this->engageManager->onUnitDestroy(unit);
}

void BasicAIModule::onUnitDiscover(BWAPI::Unit* unit)
{
  if (Broodwar->isReplay()) return;
  this->informationManager->onUnitDiscover(unit);
  this->unitGroupManager->onUnitDiscover(unit);
}
void BasicAIModule::onUnitEvade(BWAPI::Unit* unit)
{
  if (Broodwar->isReplay()) return;
  this->informationManager->onUnitEvade(unit);
  this->unitGroupManager->onUnitEvade(unit);
}

void BasicAIModule::onUnitMorph(BWAPI::Unit* unit)
{
  if (Broodwar->isReplay()) return;
  this->unitGroupManager->onUnitMorph(unit);
  this->informationManager->onUnitDiscover(unit);
}
void BasicAIModule::onUnitRenegade(BWAPI::Unit* unit)
{
  if (Broodwar->isReplay()) return;
  this->unitGroupManager->onUnitRenegade(unit);
}

void BasicAIModule::onSendText(std::string text)
{
  if (Broodwar->isReplay())
  {
    Broodwar->sendText("%s",text.c_str());
    return;
  }
  UnitType type=UnitTypes::getUnitType(text);
  if (text=="debug")
  {
    if (this->showManagerAssignments==false)
    {
      this->showManagerAssignments=true;
      this->buildOrderManager->setDebugMode(true);
      this->scoutManager->setDebugMode(true);
    }
    else
    {
      this->showManagerAssignments=false;
      this->buildOrderManager->setDebugMode(false);
      this->scoutManager->setDebugMode(false);
    }
    Broodwar->printf("%s",text.c_str());
    return;
  }
  if (text=="expand")
  {
    this->baseManager->expand();
    Broodwar->printf("%s",text.c_str());
    return;
  }
  if (type!=UnitTypes::Unknown)
  {
    this->buildOrderManager->buildAdditional(1,type,300);
  }
  else
  {
    TechType type=TechTypes::getTechType(text);
    if (type!=TechTypes::Unknown)
    {
      this->techManager->research(type);
    }
    else
    {
      UpgradeType type=UpgradeTypes::getUpgradeType(text);
      if (type!=UpgradeTypes::Unknown)
      {
        this->upgradeManager->upgrade(type);
      }
      else
        Broodwar->printf("You typed '%s'!",text.c_str());
    }
  }
  Broodwar->sendText("%s",text.c_str());
}

void BasicAIModule::evaluateExpand()
{
  // Checking for when to expand
  if (this->analyzed) {
    // Never make a hatchery before 10 food? Give it time to accumulate 300
    // because it was likely not saving. This should equate to a 12 hatch expansion
    if (Broodwar->self()->supplyUsed() > 20) {
      // First, don't expand if we haven't filled out our new drone count
      // The exception to this is if we have zero larva, we might be starved
      if (this->workerManager->getAutoBuildMax() > Broodwar->self()->allUnitCount(UnitTypes::Zerg_Drone) + 5 && Broodwar->self()->allUnitCount(UnitTypes::Zerg_Larva) > 1)
        return;

      // Second, don't expand if we just tried an expansion within the past 60s
      if (Broodwar->getFrameCount() - this->lastExpand < 60*24)
        return;

      // Make sure the BuildOrderManager can make one right now or force it if this is the first expansion
      if (this->firstExpand || buildOrderManager->hasResources(BWAPI::UnitTypes::Zerg_Hatchery)) {
        // We need to consider an in-base hatch first, but not on the first
        // expand. Also do not consider if we are gas limited or have too much larva
        int larvaCt = SelectAll(UnitTypes::Zerg_Larva).size();
        if (!firstExpand && !this->buildOrderManager->getGasLimited() && larvaCt < this->larvaThreshold) {
          // Get the number of mineral patches and vespene we have access to
          int mineral=0, gas=0;
          for each (Base* b in this->baseManager->getAllBases()) {
            mineral += b->getMinerals().size();
            gas += b->getGeysers().size();
          }
          // Calculate capacity by checking saturation
          int workerMax = this->workerManager->getAutoBuildMax();
          // If the current max goal of workers (plus padding for new hatchery
          // and a little extra) is less than what we can accomodate, we're good
          // to drop an in-base hatchery.
          if (workerMax+10 < mineral*2 + gas*3) {
            this->lastExpand = Broodwar->getFrameCount();
            this->buildOrderManager->buildAdditional(1, UnitTypes::Zerg_Hatchery, firstExpand ? 100 : 30);
            return;
          }
        }


 
        // Find a base to expand to
        for each (std::pair<BWTA::BaseLocation*, double> basePair in this->bases) {
          // Check if this is occupied by enemy or us
          if (!TheInformationManager->isEnemyBase(basePair.first) && !baseManager->getBase(basePair.first)) {
            // Skipping islands for now
            if (!basePair.first->isIsland()) {
              // Free base, let's take it
              this->lastExpand = Broodwar->getFrameCount();
              baseManager->expand(basePair.first, 100);
              this->onExpand(basePair.first);
              // only expand once per frame maximum
              return;
            }
          }
        }

        // If we're still here, it's because we couldn't find a new base, so we're
        // going to in-base hatch anyway provided we don't have excessive larva
        if (larvaCt < this->larvaThreshold) {
          // Set firstExpand to false, just in case it was still true and there
          // were no available bases to expand to
          this->firstExpand = false;
          this->lastExpand = Broodwar->getFrameCount();
          this->buildOrderManager->buildAdditional(1, UnitTypes::Zerg_Hatchery, 100);
        }
      }
    }
  }
}

void BasicAIModule::reportTime(int id, int time)
{
  int timeElapse = GetTickCount() - time;
  if (timeElapse > 10000)
      moduleMonitor[10000][id]++;
  else if(timeElapse > 1000)
      moduleMonitor[1000][id]++;
  else if(timeElapse > 55)
      moduleMonitor[55][id]++;
}

bool baseSort(const std::pair<BWTA::BaseLocation*, double> a, const std::pair<BWTA::BaseLocation*, double> b)
{
  return a.second < b.second;
}

DWORD WINAPI AnalyzeThread(LPVOID param)
{
  std::list<std::pair<BWTA::BaseLocation*, double> > *bases = (std::list<std::pair<BWTA::BaseLocation*, double> >*) param;
  BWTA::analyze();
  BasicAIModule::analyzed = true;

  // Get all bases now that data was retrieved
  BWTA::BaseLocation* home = BWTA::getStartLocation(Broodwar->self());
  bases->push_back(std::make_pair(home, 0));
  for each (BWTA::BaseLocation* base in BWTA::getBaseLocations()) {
    if (base == home)
      continue;

    double dist = home->getGroundDistance(base);
    // Disregard island expansions
    if (dist > 0) {
      bases->push_back(std::make_pair(base, dist));
    }
  }
  bases->sort(baseSort);

  // Debug: setting speed high now
  Broodwar->setLocalSpeed(5);
  return 0;
}
